package com.cmc.cloud.cmclink.doc.service.impl.si;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.api.document.req.AttachmentUpdateReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.RequirementsUpdateReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.si.*;
import com.cmc.cloud.cmclink.doc.api.document.req.so.SoForecastBaseVO;
import com.cmc.cloud.cmclink.doc.constants.CommonConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoSpiltCombineErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.*;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.CustomerRpcService;
import com.cmc.cloud.cmclink.doc.service.VgmService;
import com.cmc.cloud.cmclink.doc.service.si.BlnoService;
import com.cmc.cloud.cmclink.doc.service.si.BlnoSpiltCombineService;
import com.cmc.cloud.cmclink.doc.service.so.SoSplitCombineService;
import com.cmc.cloud.cmclink.doc.vo.blnocargovo.BlnoCargoUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnocntrvo.BlnoCntrUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnopaymentvo.BlnoPaymentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnoscnvo.BlnoScnCombineSameVO;
import com.cmc.cloud.cmclink.doc.vo.blnoscnvo.BlnoScnUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnospiltcombinevo.*;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoBaseInfoVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoBeCombineVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoInfoVO;
import com.cmc.cloud.cmclink.doc.vo.blnovslvoyvo.BlnoVslvoyUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.attachmentvo.AttachmentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.requirementsvo.RequirementsUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.vgm.VgmQueryInfoVO;
import com.cmes.framework.common.exception.ServiceException;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.framework.security.core.util.SecurityFrameworkUtils;
import liquibase.pro.packaged.L;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.cmc.cloud.cmclink.doc.api.document.emums.BookingTimeTypeEnum.SI_CHANGE_CUT_OFF;

/**
 * 提单拆并申请主 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
public class BlnoSpiltCombineServiceImpl implements BlnoSpiltCombineService {

    private static final Logger log = LoggerFactory.getLogger(BlnoSpiltCombineServiceImpl.class);
    @Resource
    private BlnoSpiltCombineMapper blnoSpiltCombineMapper;

    private static final DateTimeFormatter FORMATTER_AMEND = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    private static final DateTimeFormatter FORMATTER_AMEND2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

    private static final List<String> CAN_SPLIT_COMBINE_BLNO_STATUS = Arrays.asList(SiStatusEnum.BookingPendingSpace.getValue(),
            SiStatusEnum.AwaitingMaterialUploaded.getValue(), SiStatusEnum.BookingConfirmed.getValue());

    private static final String[] IGNORE_COLUMN = {"createTime", "updateTime", "creator", "updater"};
    @Resource
    private BlnoSpiltCombineDetailMapper combineDetailMapper;
    @Resource
    private BlnoMapper blnoMapper;
    @Resource
    private BlnoCargoMapper blnoCargoMapper;
    @Resource
    private BlnoVslvoyMapper blnoVslvoyMapper;
    @Resource
    private BlnoScnMapper blnoScnMapper;
    @Resource
    private BlnoPaymentMapper blnoPaymentMapper;
    @Resource
    private BlnoCntrMapper blnoCntrMapper;
    @Resource
    private RequirementsMapper requirementsMapper;
    @Resource
    private AttachmentMapper attachmentMapper;
    @Resource
    private VoyageBookingTimeMapper voyageBookingTimeMapper;
    @Resource
    private BlnoService blnoService;
    @Resource
    private BlnoBlPrintLogMapper blnoBlPrintLogMapper;
    @Resource
    private SoMapper soMapper;
    @Resource
    private VgmService vgmService;
    @Resource
    private CustomerRpcService customerRpcService;
    @Resource
    @Lazy
    private SoSplitCombineService soSplitCombineService;

    @Override
    public void createBlnoSpiltCombine(BlnoSpiltCombineCreateReqVO createReqVO) {
        // 插入
        BlnoSpiltCombineDO blnoSpiltCombine = BlnoSpiltCombineConvert.INSTANCE.convert(createReqVO);
        blnoSpiltCombineMapper.insert(blnoSpiltCombine);
    }

    @Override
    public void updateBlnoSpiltCombine(BlnoSpiltCombineUpdateReqVO updateReqVO) {
        // 校验存在
        validateBlnoSpiltCombineExists(updateReqVO.getId());
        // 更新
        BlnoSpiltCombineDO updateObj = BlnoSpiltCombineConvert.INSTANCE.convert(updateReqVO);
        blnoSpiltCombineMapper.updateById(updateObj);
    }

    private static final String SPLIT_COMBINE_TYPE_N = "N";
    private static final String SPLIT_COMBINE_TYPE_C = "C";

    @Override
    public void deleteBlnoSpiltCombine(Long id) {
        // 校验存在
        BlnoSpiltCombineDO spiltCombineDO = blnoSpiltCombineMapper.selectById(id);
        if (spiltCombineDO == null) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_COMBINE_NOT_EXISTS);
        }
        if (!SiSplitCombineApproveStatusEnum.PENDING.getValue().equals(spiltCombineDO.getApproveStatus())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_IS_NOT_PENDING_NOT_DELETE);
        }
        List<BlnoSpiltCombineDetailDO> spiltDetailDos = combineDetailMapper.selectList(new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>().eq(BlnoSpiltCombineDetailDO::getOperationId, spiltCombineDO.getId()));
        if (CollectionUtil.isEmpty(spiltDetailDos)) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SON_NULL_SPILT);
        }
        BlnoSpiltCombineDetailDO spiltCombineDetailDO = spiltDetailDos.get(0);
        if (BlnoSpiltCombineTypeEnum.SPLIT.getValue().equals(spiltCombineDO.getOperationType())) {
            //拆
            BlnoDO blnoDO = blnoMapper.selectById(spiltCombineDetailDO.getBlnoId());
            if (blnoDO == null) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
            }
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .eq(BlnoDO::getId, spiltCombineDetailDO.getBlnoId())
                    .set(BlnoDO::getSplitCombineType, "N->S".equals(blnoDO.getSplitCombineType()) ? "N" : "C"));
            List<Long> deleteIds = spiltDetailDos.stream().map(BlnoSpiltCombineDetailDO::getNewBlnoId).collect(Collectors.toList());
            blnoMapper.delete(new LambdaQueryWrapper<BlnoDO>()
                    .in(BlnoDO::getId, deleteIds));
        } else {
            //并
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, spiltDetailDos.stream().map(BlnoSpiltCombineDetailDO::getBlnoId).collect(Collectors.toList()))
                    .set(BlnoDO::getSplitCombineType, "N"));
            blnoMapper.delete(new LambdaQueryWrapper<BlnoDO>()
                    .eq(BlnoDO::getId, spiltCombineDetailDO.getNewBlnoId()));
        }
        // 删除
        blnoSpiltCombineMapper.deleteById(id);
        combineDetailMapper.deleteBatchIds(spiltDetailDos);
    }

    private void validateBlnoSpiltCombineExists(Long id) {
        if (blnoSpiltCombineMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_COMBINE_NOT_EXISTS);
        }
    }

    @Override
    public BlnoSpiltCombineDO getBlnoSpiltCombine(Long id) {
        return blnoSpiltCombineMapper.selectById(id);
    }

    @Override
    public List<BlnoSpiltCombineDO> getBlnoSpiltCombineList(Collection<Long> ids) {
        return blnoSpiltCombineMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<BlnoSpiltCombineDO> getBlnoSpiltCombinePage(BlnoSpiltCombinePageReqVO reqVO) {
        return null;
    }

    @Override
    public PageResult<BlnoSpiltCombinePageRespVO> getSpiltCombinePage(BlnoSpiltCombinePageReqVO reqVO) {
        IPage<Long> page = Page.of(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<Long> result = blnoSpiltCombineMapper.selectPageList(page, reqVO);
        if (result.getTotal() > 0) {
            List<BlnoSpiltCombinePageRespVO> list = blnoSpiltCombineMapper.getSpiltCombineInfo(result.getRecords());
            List<String> codes = list.stream().map(BlnoSpiltCombinePageRespVO::getBkpCode).collect(Collectors.toList());
            Map<String, String> companyNameMap = customerRpcService.getCompanyLocalNameMap(codes);
            list.forEach(n->{
                n.setBkpLocalName(companyNameMap.getOrDefault(n.getBkpCode(),n.getBkpCode()));
            });
            return new PageResult<BlnoSpiltCombinePageRespVO>(list, result.getTotal());
        }
        return new PageResult<BlnoSpiltCombinePageRespVO>(new ArrayList<BlnoSpiltCombinePageRespVO>(), 0L);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long commitBlnoSpilt(BlnoSpiltVO blnoSpiltVO) {
        BlnoInfoVO blnoInfoVO = checkOriginalBlno(blnoSpiltVO);
        BlnoBaseInfoVO blnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        List<BlnoCargoUpdateReqVO> blnoCargos = blnoInfoVO.getBlnoCargos();

        Boolean approve = isDirectApprove(blnoBasInfoVO);
        deleteBlnoPrintLog(approve, Collections.singletonList(blnoBasInfoVO.getId()));
        blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>().eq(BlnoDO::getId, blnoBasInfoVO.getId())
                .set(BlnoDO::getSplitCombineType, SPLIT_COMBINE_TYPE_C.equals(blnoBasInfoVO.getSplitCombineType()) ? "C->S" : "N->S")
                .set(BlnoDO::getPodBlFlag, !approve)
                .set(BlnoDO::getSiStatus, approve ? SiStatusEnum.BookingPendingSpace.getValue() : blnoBasInfoVO.getSiStatus()));
        BlnoSpiltCombineDO spiltCombineDO = createBlnoSpilt(BlnoSpiltCombineTypeEnum.SPLIT.getValue(),
                approve ? SiSplitCombineApproveStatusEnum.APPROVED.getValue() : SiSplitCombineApproveStatusEnum.PENDING.getValue());
        List<BlnoDO> blnoDos = new ArrayList<>();
        List<BlnoCargoDO> cargoDos = new ArrayList<>();
        List<BlnoCntrDO> blnoCntrDos = new ArrayList<>();
        List<RequirementsDO> requireDos = new ArrayList<>();
        List<AttachmentDO> attachmentDos = new ArrayList<>();
        List<BlnoVslvoyDO> vslvoyDos = new ArrayList<>();
        List<BlnoScnDO> scnDos = new ArrayList<>();
        List<BlnoPaymentDO> paymentDos = new ArrayList<>();

        List<BlnoSpiltCombineDetailDO> blnoSpiltCombineDetailDos = new ArrayList<>();

        Map<String, List<BlnoCargoDO>> cargoMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoCntrDO>> cntrMap = MapUtil.newHashMap(20);
        Map<String, List<RequirementsDO>> requireMap = MapUtil.newHashMap(20);
        Map<String, List<AttachmentDO>> attachmentMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoVslvoyDO>> vslvoyMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoScnDO>> scnMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoPaymentDO>> paymentMap = MapUtil.newHashMap(20);

        for (BlnoSpiltChildVO blnoSpiltChildVO : blnoSpiltVO.getSpiltChildVos()) {
            BlnoDO blnoDO = BeanUtil.copyProperties(blnoBasInfoVO, BlnoDO.class);
            blnoDO.setPodBlFlag(approve);
            blnoDO.setSiStatus(approve ? SiStatusEnum.BookingPendingSpace.getValue() : blnoBasInfoVO.getSiStatus());
            blnoDO.setSplitCombineType("S");
            blnoDO.setBlNo(blnoSpiltChildVO.getBlNo());
            blnoDO.setId(null);
            blnoDos.add(blnoDO);

            BlnoCargoUpdateReqVO cargoChild = blnoSpiltChildVO.getCargoChild();
            List<BlnoCargoDO> innerCargoDos = new ArrayList<>();
            BlnoCargoDO blnoCargoDO = BeanUtil.copyProperties(blnoCargos.get(0), BlnoCargoDO.class, "createTime",
                    "updateTime", "creator", "updater");
            blnoCargoDO.setId(null);
            blnoCargoDO.setQuantity(cargoChild.getQuantity());
            blnoCargoDO.setGrossWeight(cargoChild.getGrossWeight());
            blnoCargoDO.setMeasurement(cargoChild.getMeasurement());
            innerCargoDos.add(blnoCargoDO);
            cargoDos.addAll(innerCargoDos);
            cargoMap.put(blnoSpiltChildVO.getBlNo(), innerCargoDos);

            List<BlnoCntrDO> innerBlnoCntrDos = new ArrayList<>();
            blnoSpiltChildVO.getCntrSpiltChild().stream().forEach(n -> {
                BlnoCntrDO blnoCntrDO =
                        BeanUtil.copyProperties(n, BlnoCntrDO.class, IGNORE_COLUMN);
                blnoCntrDO.setId(null);
                innerBlnoCntrDos.add(blnoCntrDO);
            });
            blnoCntrDos.addAll(innerBlnoCntrDos);
            cntrMap.put(blnoSpiltChildVO.getBlNo(), innerBlnoCntrDos);

            List<RequirementsDO> innerRrquireDos = new ArrayList<>();
            blnoInfoVO.getReqVOList().stream().forEach(n -> {
                RequirementsDO requirementsDO =
                        BeanUtil.copyProperties(n, RequirementsDO.class, IGNORE_COLUMN);
                requirementsDO.setId(null);
                innerRrquireDos.add(requirementsDO);
            });
            requireDos.addAll(innerRrquireDos);
            requireMap.put(blnoSpiltChildVO.getBlNo(), innerRrquireDos);

            List<AttachmentDO> innerAttachmentDos = new ArrayList<>();
            blnoInfoVO.getAttachmentReqVO().stream().forEach(n -> {
                AttachmentDO attachmentDO =
                        BeanUtil.copyProperties(n, AttachmentDO.class, IGNORE_COLUMN);
                attachmentDO.setId(null);
                innerAttachmentDos.add(attachmentDO);
            });
            attachmentDos.addAll(innerAttachmentDos);
            attachmentMap.put(blnoSpiltChildVO.getBlNo(), innerAttachmentDos);

            List<BlnoVslvoyDO> innerVslvoyDos = new ArrayList<>();
            blnoInfoVO.getBlnoVslvoys().stream().forEach(n -> {
                BlnoVslvoyDO blnoVslvoyDO =
                        BeanUtil.copyProperties(n, BlnoVslvoyDO.class, IGNORE_COLUMN);
                blnoVslvoyDO.setId(null);
                innerVslvoyDos.add(blnoVslvoyDO);
            });
            vslvoyDos.addAll(innerVslvoyDos);
            vslvoyMap.put(blnoSpiltChildVO.getBlNo(), innerVslvoyDos);

            List<BlnoScnDO> innerScnDos = new ArrayList<>();
            blnoInfoVO.getBlnoScns().stream().forEach(n -> {
                BlnoScnDO blnoScnDO =
                        BeanUtil.copyProperties(n, BlnoScnDO.class, IGNORE_COLUMN);
                blnoScnDO.setId(null);
                innerScnDos.add(blnoScnDO);
            });
            scnDos.addAll(innerScnDos);
            scnMap.put(blnoSpiltChildVO.getBlNo(), innerScnDos);

            List<BlnoPaymentDO> innerPaymentDos = new ArrayList<>();
            blnoInfoVO.getBlnoPayments().stream().forEach(n -> {
                BlnoPaymentDO blnoPaymentDO =
                        BeanUtil.copyProperties(n, BlnoPaymentDO.class, IGNORE_COLUMN);
                blnoPaymentDO.setId(null);
                innerPaymentDos.add(blnoPaymentDO);
            });
            paymentDos.addAll(innerPaymentDos);
            paymentMap.put(blnoSpiltChildVO.getBlNo(), innerPaymentDos);
        }

        blnoMapper.insertBatch(blnoDos);
        List<String> blNos = blnoDos.stream().map(BlnoDO::getBlNo).collect(Collectors.toList());
        List<BlnoDO> newBlNos = blnoMapper.selectList(
                new LambdaQueryWrapperX<BlnoDO>().eq(BlnoDO::getSplitCombineType, "S").in(BlnoDO::getBlNo, blNos).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        Map<String, Long> noIdMap = newBlNos.stream().collect(Collectors.toMap(BlnoDO::getBlNo, BlnoDO::getId));

        for (String blno : noIdMap.keySet()) {
            Long newBlnoId = noIdMap.get(blno);

            BlnoSpiltCombineDetailDO blnoSpiltCombineDetailDO = new BlnoSpiltCombineDetailDO();
            blnoSpiltCombineDetailDO.setOperationId(spiltCombineDO.getId());
            blnoSpiltCombineDetailDO.setBlnoId(blnoBasInfoVO.getId());
            blnoSpiltCombineDetailDO.setNewBlnoId(newBlnoId);
            blnoSpiltCombineDetailDos.add(blnoSpiltCombineDetailDO);

            // addAll 引用拷贝 外层集合跟着一起修改
            cargoMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            cntrMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            requireMap.get(blno).forEach(n -> n.setReferenceId(newBlnoId));
            attachmentMap.get(blno).forEach(n -> n.setReferenceId(newBlnoId));
            vslvoyMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            scnMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            paymentMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
        }

        combineDetailMapper.insertBatch(blnoSpiltCombineDetailDos);
        blnoCargoMapper.insertBatch(cargoDos);
        blnoVslvoyMapper.insertBatch(vslvoyDos);
        blnoScnMapper.insertBatch(scnDos);
        blnoPaymentMapper.insertBatch(paymentDos);
        blnoCntrMapper.insertBatch(blnoCntrDos);
        requirementsMapper.insertBatch(requireDos);
        attachmentMapper.insertBatch(attachmentDos);
        return spiltCombineDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long commitBlnoSpiltNew(BlnoSpiltVO blnoSpiltVO) {
        BlnoInfoVO blnoInfoVO = checkOriginalBlno(blnoSpiltVO);
        BlnoBaseInfoVO blnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        List<BlnoCargoUpdateReqVO> blnoCargos = blnoInfoVO.getBlnoCargos();

        blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>().eq(BlnoDO::getId, blnoBasInfoVO.getId())
                .set(BlnoDO::getSplitCombineType, SPLIT_COMBINE_TYPE_C.equals(blnoBasInfoVO.getSplitCombineType()) ?
                        SiSplitCombineEnum.COMBINE_NORMAL.getValue() : SiSplitCombineEnum.NORMAL_SPLIT.getValue()));

        BlnoSpiltCombineDO spiltCombineDO = createBlnoSpilt(BlnoSpiltCombineTypeEnum.SPLIT.getValue(),
                SiSplitCombineApproveStatusEnum.PENDING.getValue());
        List<BlnoDO> blnoDos = new ArrayList<>();
        List<BlnoCargoDO> cargoDos = new ArrayList<>();
        List<BlnoCntrDO> blnoCntrDos = new ArrayList<>();
        List<RequirementsDO> requireDos = new ArrayList<>();
        List<AttachmentDO> attachmentDos = new ArrayList<>();
        List<BlnoVslvoyDO> vslvoyDos = new ArrayList<>();
        List<BlnoScnDO> scnDos = new ArrayList<>();
        List<BlnoPaymentDO> paymentDos = new ArrayList<>();

        List<BlnoSpiltCombineDetailDO> blnoSpiltCombineDetailDos = new ArrayList<>();

        Map<String, List<BlnoCargoDO>> cargoMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoCntrDO>> cntrMap = MapUtil.newHashMap(20);
        Map<String, List<RequirementsDO>> requireMap = MapUtil.newHashMap(20);
        Map<String, List<AttachmentDO>> attachmentMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoVslvoyDO>> vslvoyMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoScnDO>> scnMap = MapUtil.newHashMap(20);
        Map<String, List<BlnoPaymentDO>> paymentMap = MapUtil.newHashMap(20);

        for (BlnoSpiltChildVO blnoSpiltChildVO : blnoSpiltVO.getSpiltChildVos()) {
            BlnoDO blnoDO = BeanUtil.copyProperties(blnoBasInfoVO, BlnoDO.class);
            blnoDos.add(setBlno(blnoDO,blnoSpiltChildVO));
            //货物
            BlnoCargoUpdateReqVO cargoChild = blnoSpiltChildVO.getCargoChild();
            List<BlnoCargoDO> innerCargoDos = new ArrayList<>();
            BlnoCargoDO blnoCargoDO = BeanUtil.copyProperties(cargoChild, BlnoCargoDO.class);
            blnoCargoDO.setId(null);
            innerCargoDos.add(blnoCargoDO);
            cargoDos.addAll(innerCargoDos);
            cargoMap.put(blnoSpiltChildVO.getBlNo(), innerCargoDos);
            //箱
            List<BlnoCntrDO> innerBlnoCntrDos = new ArrayList<>();
            blnoSpiltChildVO.getCntrSpiltChild().stream().forEach(n -> {
                BlnoCntrDO blnoCntrDO =
                        BeanUtil.copyProperties(n, BlnoCntrDO.class, IGNORE_COLUMN);
                blnoCntrDO.setId(null);
                innerBlnoCntrDos.add(blnoCntrDO);
            });
            blnoCntrDos.addAll(innerBlnoCntrDos);
            cntrMap.put(blnoSpiltChildVO.getBlNo(), innerBlnoCntrDos);
            //订舱要求
            List<RequirementsDO> innerRrquireDos = new ArrayList<>();
            if(CollectionUtil.isNotEmpty(blnoSpiltChildVO.getRequirementsSpiltChild())){
                blnoSpiltChildVO.getRequirementsSpiltChild().stream().forEach(n -> {
                    RequirementsDO requirementsDO =
                            BeanUtil.copyProperties(n, RequirementsDO.class, IGNORE_COLUMN);
                    requirementsDO.setId(null);
                    requirementsDO.setReferenceType(SoSiEnum.SI.getValue());
                    innerRrquireDos.add(requirementsDO);
                });
                requireDos.addAll(innerRrquireDos);
                requireMap.put(blnoSpiltChildVO.getBlNo(), innerRrquireDos);
            }

            //附件
            if(CollectionUtil.isNotEmpty(blnoSpiltChildVO.getAttachmentsSpiltChild())){
                List<AttachmentDO> innerAttachmentDos = new ArrayList<>();
                blnoSpiltChildVO.getAttachmentsSpiltChild().forEach(attatchment -> {
                    AttachmentDO attachmentDO = BeanUtil.copyProperties(attatchment, AttachmentDO.class, IGNORE_COLUMN);
                    attachmentDO.setId(null);
                    attachmentDO.setReferenceType(SoSiEnum.SI.getValue());
                    innerAttachmentDos.add(attachmentDO);
                });
                attachmentDos.addAll(innerAttachmentDos);
                attachmentMap.put(blnoSpiltChildVO.getBlNo(), innerAttachmentDos);
            }

            //航线船期
            List<BlnoVslvoyDO> innerVslvoyDos = new ArrayList<>();
            blnoInfoVO.getBlnoVslvoys().stream().forEach(n -> {
                BlnoVslvoyDO blnoVslvoyDO =
                        BeanUtil.copyProperties(n, BlnoVslvoyDO.class, IGNORE_COLUMN);
                blnoVslvoyDO.setId(null);
                innerVslvoyDos.add(blnoVslvoyDO);
            });
            vslvoyDos.addAll(innerVslvoyDos);
            vslvoyMap.put(blnoSpiltChildVO.getBlNo(), innerVslvoyDos);
            //子单收发通信息
            List<BlnoScnDO> innerScnDos = new ArrayList<>();
            //子单支付方式信息
            List<BlnoPaymentDO> innerPaymentDos = new ArrayList<>();
            //更新收发通信息
            blnoSpiltChildVO.getScnSpiltChild().forEach(scn -> {
                BlnoScnDO blnoScnDO = BeanUtil.copyProperties(scn, BlnoScnDO.class, IGNORE_COLUMN);
                blnoScnDO.setId(null);
                innerScnDos.add(blnoScnDO);
            });
            scnDos.addAll(innerScnDos);
            scnMap.put(blnoSpiltChildVO.getBlNo(), innerScnDos);
            //更新支付方式信息
            blnoSpiltChildVO.getPaymentSpiltChild().forEach(payment -> {
                BlnoPaymentDO blnoPaymentDO = BeanUtil.copyProperties(payment, BlnoPaymentDO.class, IGNORE_COLUMN);
                blnoPaymentDO.setId(null);
                innerPaymentDos.add(blnoPaymentDO);
            });
            paymentDos.addAll(innerPaymentDos);
            paymentMap.put(blnoSpiltChildVO.getBlNo(), innerPaymentDos);
        }
        blnoMapper.insertBatch(blnoDos);
        List<String> blNos = blnoDos.stream().map(BlnoDO::getBlNo).collect(Collectors.toList());
        List<BlnoDO> newBlNos = blnoMapper.selectList(
                new LambdaQueryWrapperX<BlnoDO>().eq(BlnoDO::getSplitCombineType, "S").in(BlnoDO::getBlNo, blNos).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        Map<String, Long> noIdMap = newBlNos.stream().collect(Collectors.toMap(BlnoDO::getBlNo, BlnoDO::getId));

        for (String blno : noIdMap.keySet()) {
            Long newBlnoId = noIdMap.get(blno);

            BlnoSpiltCombineDetailDO blnoSpiltCombineDetailDO = new BlnoSpiltCombineDetailDO();
            blnoSpiltCombineDetailDO.setOperationId(spiltCombineDO.getId());
            blnoSpiltCombineDetailDO.setBlnoId(blnoBasInfoVO.getId());
            blnoSpiltCombineDetailDO.setNewBlnoId(newBlnoId);
            blnoSpiltCombineDetailDos.add(blnoSpiltCombineDetailDO);

            // addAll 引用拷贝 外层集合跟着一起修改
            cargoMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            cntrMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            if(CollectionUtil.isNotEmpty(attachmentMap.get(blno))){
                attachmentMap.get(blno).forEach(n -> n.setReferenceId(newBlnoId));
            }
            if(CollectionUtil.isNotEmpty(requireMap.get(blno))){
                requireMap.get(blno).forEach(n -> n.setReferenceId(newBlnoId));
            }
            vslvoyMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            scnMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
            paymentMap.get(blno).forEach(n -> n.setBlnoId(newBlnoId));
        }

        combineDetailMapper.insertBatch(blnoSpiltCombineDetailDos);
        blnoCargoMapper.insertBatch(cargoDos);
        blnoVslvoyMapper.insertBatch(vslvoyDos);
        blnoScnMapper.insertBatch(scnDos);
        blnoPaymentMapper.insertBatch(paymentDos);
        blnoCntrMapper.insertBatch(blnoCntrDos);
        requirementsMapper.insertBatch(requireDos);
        attachmentMapper.insertBatch(attachmentDos);
        //原单的修改
        return spiltCombineDO.getId();
    }

    private BlnoDO setBlno (BlnoDO blnoDO,BlnoSpiltChildVO blnoSpiltChildVO){
        //拆分的子单能修改的主信息
        blnoDO.setBlNo(blnoSpiltChildVO.getBlNo());
        blnoDO.setSignOrder(blnoSpiltChildVO.getSignOrder());
        blnoDO.setBlType(blnoSpiltChildVO.getBlType());
        blnoDO.setPrintBlNumber(blnoSpiltChildVO.getPrintBlNumber());
        blnoDO.setPorDesc(blnoSpiltChildVO.getPorDesc());
        blnoDO.setPolDesc(blnoSpiltChildVO.getPolDesc());
        blnoDO.setPodDesc(blnoSpiltChildVO.getPodDesc());
        blnoDO.setDelDesc(blnoSpiltChildVO.getDelDesc());
        blnoDO.setPodBlFlag(false);
        blnoDO.setSplitCombineType(SiSplitCombineEnum.SPLIT.getValue());
        blnoDO.setSignOrderTime(LocalDateTime.now());
        blnoDO.setId(null);
        return blnoDO;
    }

    private Boolean isDirectApprove(BlnoBaseInfoVO blnoBasInfoVO) {
        VoyageBookingTimeDO voyageBookingTimeDO;
        try {
            voyageBookingTimeDO = voyageBookingTimeMapper.selectOne(new LambdaQueryWrapperX<VoyageBookingTimeDO>()
                    .eq(VoyageBookingTimeDO::getExImType, ExImTypeEnum.EX.getValue())
                    .eq(VoyageBookingTimeDO::getTimeType, SI_CHANGE_CUT_OFF.getValue())
                    .eq(VoyageBookingTimeDO::getPodCode, blnoBasInfoVO.getPolCode())
                    .eq(VoyageBookingTimeDO::getVesselCode, blnoBasInfoVO.getBkgVesselCode())
                    .eq(VoyageBookingTimeDO::getVoyage, blnoBasInfoVO.getBkgVoyage()));
        } catch (Exception e) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_BOOKING_TIME_ERROR);
        }
        if (voyageBookingTimeDO == null) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_BOOKING_TIME_NOT_EXISTED);
        } else {
            LocalDateTime localDateTime = LocalDateTime.parse(voyageBookingTimeDO.getTimeValue(), FORMATTER_AMEND2);
            // 拆并直接 申请通过 &&  删除原提单 的提单打印记录
            return localDateTime.isAfter(LocalDateTime.now());
        }
    }

    private void deleteBlnoPrintLog(Boolean isDirectApprove, List<Long> blnoIds) {
        if (isDirectApprove) {
            blnoBlPrintLogMapper.delete(new LambdaQueryWrapper<BlnoBlPrintLogDO>().in(BlnoBlPrintLogDO::getBlnoId, blnoIds));
        }
    }

    private BlnoSpiltCombineDO createBlnoSpilt(String operateType, String approveStatus) {
        BlnoSpiltCombineDO spiltCombineDO = new BlnoSpiltCombineDO();
        spiltCombineDO.setOperationNo("OPERATIONS" + LocalDateTime.now().format(FORMATTER_AMEND));
        spiltCombineDO.setOperationType(operateType);
        spiltCombineDO.setApproveStatus(approveStatus);
        spiltCombineDO.setApplyTime(LocalDateTime.now());
        spiltCombineDO.setApplyUser(String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        blnoSpiltCombineMapper.insert(spiltCombineDO);
        return spiltCombineDO;
    }

    private BlnoInfoVO checkOriginalBlno(BlnoSpiltVO blnoSpiltVO) {
        BlnoInfoVO siDetail = blnoService.getSiDetail(blnoSpiltVO.getOriginalBlnoId());
        List<BlnoSpiltChildVO> spiltChildVos = blnoSpiltVO.getSpiltChildVos();
        if (siDetail == null || siDetail.getBlnoBasInfoVO() == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        BlnoBaseInfoVO blnoBasInfoVO = siDetail.getBlnoBasInfoVO();
        if (!StringUtils.equals(SPLIT_COMBINE_TYPE_N, blnoBasInfoVO.getSplitCombineType())
                && !StringUtils.equals(SPLIT_COMBINE_TYPE_C, blnoBasInfoVO.getSplitCombineType())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_STATUS_ERROR);
        }
        if (spiltChildVos.stream().map(BlnoSpiltChildVO::getBlNo).distinct().count() != spiltChildVos.size()) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_REPEAT_NO_ERROR);
        }
        /*if (!StringUtils.equals(blnoBasInfoVO.getSiStatus(), SiStatusEnum.BookingPendingSpace.getValue())
                && !StringUtils.equals(blnoBasInfoVO.getSiStatus(), SiStatusEnum.AwaitingMaterialUploaded.getValue())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SI_STATUS_NOT_SPILT);
        }*/
        //SI可以拆单的状态 SI已确认 待确认Draft件 已确认Draft件
        if (!CAN_SPLIT_COMBINE_BLNO_STATUS.contains(blnoBasInfoVO.getSiStatus())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SI_STATUS_NOT_SPILT);
        }
        if (StringUtils.isNotEmpty(blnoBasInfoVO.getSignType())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SIGNED_NOT_SPILT);
        }
        if (StringUtils.isNotEmpty(blnoBasInfoVO.getDoStatus())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_DO_NOT_SPILT);
        }
        if (CollectionUtil.isEmpty(spiltChildVos)) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SON_NULL_NOT_SPILT);
        } else {
            if (spiltChildVos.stream()
                    .anyMatch(element -> !element.getSplitCombineType().equals(SiSplitCombineEnum.SPLIT.getValue()))) {
                throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SON_STATUS_NOT_SPILT);
            }
        }
        BlnoCargoUpdateReqVO blnoCargoUpdateReqVO = siDetail.getBlnoCargos().get(0);
        if (blnoSpiltVO.getSpiltChildVos().stream().anyMatch(n -> !n.getCargoChild().getCargoType().equals(blnoCargoUpdateReqVO.getCargoType()))) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_CARGO_DIFFERENT);
        }

        List<BlnoCntrUpdateReqVO> blnoCntrs = siDetail.getBlnoCntrs();
        Map<String, BlnoCntrUpdateReqVO> originalCntrMap =
                blnoCntrs.stream().collect(Collectors.toMap(BlnoCntrUpdateReqVO::getCntrNo, n -> n));

        // 判断 货物 件 重 尺 和 箱 汇总 是否一致 以及 拆分结果 汇总是否一致
        spiltChildVos.forEach(n -> {
            BlnoCargoUpdateReqVO cargoChild = n.getCargoChild();
            if (cargoChild != null) {
                if (cargoChild.getQuantity()
                        .compareTo(n.getCntrSpiltChild().stream().mapToInt(BlnoCntrUpdateReqVO::getQuantity).sum()) != 0) {
                    throw ServiceExceptionUtil
                            .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_SON_QUANITY_NOT_EQUAL);
                }
                if (cargoChild.getGrossWeight().compareTo(n.getCntrSpiltChild().stream().map(BlnoCntrUpdateReqVO::getGrossWeight)
                        .reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                    throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_SON_WEIGHT_NOT_EQUAL);
                }
                if (cargoChild.getMeasurement().compareTo(n.getCntrSpiltChild().stream().map(BlnoCntrUpdateReqVO::getMeasurement)
                        .reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                    throw ServiceExceptionUtil
                            .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_SON_MEASUREMENT_NOT_EQUAL);
                }
            }
        });
        spiltChildVos.forEach(n -> {
            List<BlnoScnUpdateReqVO> scnSpiltChild = n.getScnSpiltChild();

            n.getCntrSpiltChild().forEach(e -> {
                e.setSplitBlNo(n.getBlNo());
                e.setSplitSignOrder(n.getSignOrder());
                scnSpiltChild.stream().filter(m -> PartyTypeCodeEnum.CONSIGNEE.getValue().equals(m.getPartyTypeCode())).findFirst()
                        .ifPresent(m -> e.setSplitReceiver(m.getCompanyName()));
            });

        });
        // 判断拼箱标记 的 子箱 和 父箱的合计是否一致
        List<BlnoCntrUpdateReqVO> blnoCntrUpdateReqVos = new ArrayList<>();
        spiltChildVos.forEach(n -> blnoCntrUpdateReqVos.addAll(n.getCntrSpiltChild()));
        if (blnoCntrUpdateReqVos.stream().anyMatch(n -> n.getLcl() == true)) {
            Map<String, List<BlnoCntrUpdateReqVO>> cntrMap = blnoCntrUpdateReqVos.stream()
                    .filter(t -> t.getLcl() == true).collect(Collectors.groupingBy(BlnoCntrUpdateReqVO::getCntrNo));
            for (String cntrNo : originalCntrMap.keySet()) {
                BlnoCntrUpdateReqVO cntrUpdateReqVO = originalCntrMap.get(cntrNo);
                List<BlnoCntrUpdateReqVO> newCntrs = cntrMap.get(cntrNo);

                if (CollectionUtil.isNotEmpty(newCntrs)) {

                    //如果原单 该箱已经和 其他提单还没拼箱过  那么子单里面不能只有一个箱打了拼箱标记
                    if (!(cntrUpdateReqVO.getLcl() == null ? Boolean.FALSE : cntrUpdateReqVO.getLcl())) {
                        if (newCntrs.stream().filter(n -> n.getLcl() == true).map(BlnoCntrUpdateReqVO::getCntrNo).count() == 1) {
                            throw ServiceExceptionUtil
                                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_IS_PING_XIANG_ERROR);
                        }
                    }

                    // 一箱多票提单的收货人必须一致、一箱多票提单签单方式必须一致
                    if (newCntrs.stream().map(BlnoCntrUpdateReqVO::getSplitSignOrder).distinct().count() != 1) {
                        throw ServiceExceptionUtil
                                .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_LCL_SIGN_ORDER);
                    }
                    if (newCntrs.stream().map(BlnoCntrUpdateReqVO::getSplitReceiver).distinct().count() != 1) {
                        throw ServiceExceptionUtil
                                .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_LCL_RECEIVER);
                    }

                    //拼箱数据合计校验
                    if (cntrUpdateReqVO.getQuantity()
                            .compareTo(newCntrs.stream().mapToInt(BlnoCntrUpdateReqVO::getQuantity).sum()) != 0) {
                        throw ServiceExceptionUtil
                                .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_QUANITY_NOT_EQUAL);
                    }
                    if (cntrUpdateReqVO.getGrossWeight().compareTo(newCntrs.stream()
                            .map(BlnoCntrUpdateReqVO::getGrossWeight).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                        throw ServiceExceptionUtil
                                .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_WEIGHT_NOT_EQUAL);
                    }
                    if (cntrUpdateReqVO.getMeasurement().compareTo(newCntrs.stream()
                            .map(BlnoCntrUpdateReqVO::getMeasurement).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                        throw ServiceExceptionUtil
                                .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_MEASUREMENT_NOT_EQUAL);
                    }
                }
            }
        }
        // 新提单 件重尺汇总 和 原提单校验
        compareCargo(blnoCargoUpdateReqVO, spiltChildVos);
        return siDetail;
    }

    private void compareCargo(BlnoCargoUpdateReqVO blnoCargoUpdateReqVO, List<BlnoSpiltChildVO> spiltChildVos) {
        if (blnoCargoUpdateReqVO.getQuantity()
                .compareTo(spiltChildVos.stream().mapToInt(n->n.getCargoChild().getQuantity()).sum()) != 0) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_QUANITY_NOT_EQUAL);
        }
        if (blnoCargoUpdateReqVO.getGrossWeight().compareTo(spiltChildVos.stream()
                .map(n->n.getCargoChild().getGrossWeight()).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_WEIGHT_NOT_EQUAL);
        }
        if (blnoCargoUpdateReqVO.getMeasurement().compareTo(spiltChildVos.stream()
                .map(n->n.getCargoChild().getMeasurement()).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_MEASUREMENT_NOT_EQUAL);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long commitBlnoCombine(BlnoCombineVO blnoCombineVO) {
        List<BlnoCombineChildVO> spiltChildVos = blnoCombineVO.getSpiltChildVos();
        BlnoBeCombineVO blnoBeCombineVO = blnoCombineVO.getBlnoBeCombineVO();

        List<BlnoCntrUpdateReqVO> cntrCombineChild = blnoCombineVO.getCntrCombineChild();
        //获取第一条数据的信息
        BlnoInfoVO blnoInfoVO = blnoService.getSiDetail(spiltChildVos.get(0).getBlnoId());
        BlnoBaseInfoVO oldBlnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        //是否直接审批通过
        Boolean approve = isDirectApprove(oldBlnoBasInfoVO);
        //合并校验
        List<Long> oldSiIds = checkCombine(spiltChildVos, cntrCombineChild, approve);
        deleteBlnoPrintLog(approve, oldSiIds);

        BlnoSpiltCombineDO spiltCombineDO = createBlnoSpilt(BlnoSpiltCombineTypeEnum.COMBINE.getValue(),
                approve ? SiSplitCombineApproveStatusEnum.APPROVED.getValue() : SiSplitCombineApproveStatusEnum.PENDING.getValue());
        //创建新si
        BlnoDO blnoDO = BeanUtil.copyProperties(oldBlnoBasInfoVO, BlnoDO.class, IGNORE_COLUMN);
        blnoDO.setId(null);
        blnoDO.setBlNo(blnoBeCombineVO.getBlNo());
        blnoDO.setSplitCombineType("C");
        blnoDO.setPodBlFlag(approve);
        blnoDO.setSiStatus(approve ? SiStatusEnum.BookingPendingSpace.getValue() : blnoDO.getSiStatus());
        blnoMapper.insert(blnoDO);
        Long newBlnoId = blnoDO.getId();

        //新 si 货物信息
        BlnoCargoDO blnoCargoDO = BeanUtil.copyProperties(blnoInfoVO.getBlnoCargos().get(0), BlnoCargoDO.class, IGNORE_COLUMN);
        blnoCargoDO.setId(null);
        blnoCargoDO.setBlnoId(newBlnoId);
        blnoCargoDO.setQuantity(cntrCombineChild.stream().mapToInt(BlnoCntrUpdateReqVO::getQuantity).sum());
        blnoCargoDO.setGrossWeight(cntrCombineChild.stream().map(BlnoCntrUpdateReqVO::getGrossWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
        blnoCargoDO.setMeasurement(cntrCombineChild.stream().map(BlnoCntrUpdateReqVO::getMeasurement).reduce(BigDecimal.ZERO, BigDecimal::add));
        //网厅可修改货物的唛头，货物描述，中文品名，HSCODE
        if (Objects.equals(SoDataSourceEnum.WEB.getValue(),blnoCombineVO.getCombineDataSource())){
            BlnoCargoUpdateReqVO blnoCargoCombine = blnoCombineVO.getBlnoCargoCombine();
            blnoCargoDO.setMarks(blnoCargoCombine.getMarks());
            blnoCargoDO.setCommodityEn(blnoCargoCombine.getCommodityEn());
            blnoCargoDO.setHsCode(blnoCargoCombine.getHsCode());
            blnoCargoDO.setCommodityCn(blnoCargoCombine.getCommodityCn());
        }
        blnoCargoMapper.insert(blnoCargoDO);

        // 拆合并详情记录
        List<BlnoSpiltCombineDetailDO> blnoSpiltCombineDetailDos = new ArrayList<>();
        oldSiIds.forEach(n -> {
                    BlnoSpiltCombineDetailDO blnoSpiltCombineDetailDO = new BlnoSpiltCombineDetailDO();
                    blnoSpiltCombineDetailDO.setNewBlnoId(newBlnoId);
                    blnoSpiltCombineDetailDO.setOperationId(spiltCombineDO.getId());
                    blnoSpiltCombineDetailDO.setBlnoId(n);
                    blnoSpiltCombineDetailDos.add(blnoSpiltCombineDetailDO);
                }
        );

        //新 si 箱信息
        List<BlnoCntrDO> blnoCntrDos = BeanUtil.copyToList(cntrCombineChild, BlnoCntrDO.class);
        blnoCntrDos.forEach(n -> {
            n.setBlnoId(newBlnoId);
            n.setId(null);
            n.setUpdater(null);
            n.setUpdateTime(null);
            n.setCreator(null);
            n.setCreateTime(null);
        });
        blnoCntrMapper.insertBatch(blnoCntrDos);

        List<RequirementsDO> innerRrquireDos = new ArrayList<>();
        if (Objects.equals(SoDataSourceEnum.WEB.getValue(), blnoCombineVO.getCombineDataSource())) {
            blnoCombineVO.getAttachmentCombines().stream().forEach(n -> {
                RequirementsDO requirementsDO =
                        BeanUtil.copyProperties(n, RequirementsDO.class, IGNORE_COLUMN);
                requirementsDO.setId(null);
                requirementsDO.setReferenceId(newBlnoId);
                innerRrquireDos.add(requirementsDO);
            });
        } else {
            blnoInfoVO.getReqVOList().stream().forEach(n -> {
                RequirementsDO requirementsDO =
                        BeanUtil.copyProperties(n, RequirementsDO.class, IGNORE_COLUMN);
                requirementsDO.setId(null);
                requirementsDO.setReferenceId(newBlnoId);
                innerRrquireDos.add(requirementsDO);
            });
        }

        List<AttachmentDO> innerAttachmentDos = new ArrayList<>();
        blnoInfoVO.getAttachmentReqVO().stream().forEach(n -> {
            AttachmentDO attachmentDO =
                    BeanUtil.copyProperties(n, AttachmentDO.class, IGNORE_COLUMN);
            attachmentDO.setId(null);
            attachmentDO.setReferenceId(newBlnoId);
            innerAttachmentDos.add(attachmentDO);
        });

        List<BlnoVslvoyDO> innerVslvoyDos = new ArrayList<>();
        blnoInfoVO.getBlnoVslvoys().stream().forEach(n -> {
            BlnoVslvoyDO blnoVslvoyDO =
                    BeanUtil.copyProperties(n, BlnoVslvoyDO.class, IGNORE_COLUMN);
            blnoVslvoyDO.setId(null);
            blnoVslvoyDO.setBlnoId(newBlnoId);
            innerVslvoyDos.add(blnoVslvoyDO);
        });

        List<BlnoScnDO> innerScnDos = new ArrayList<>();
        blnoInfoVO.getBlnoScns().stream().forEach(n -> {
            BlnoScnDO blnoScnDO =
                    BeanUtil.copyProperties(n, BlnoScnDO.class, IGNORE_COLUMN);
            blnoScnDO.setId(null);
            blnoScnDO.setBlnoId(newBlnoId);
            innerScnDos.add(blnoScnDO);
        });

        List<BlnoPaymentDO> innerPaymentDos = new ArrayList<>();
        blnoInfoVO.getBlnoPayments().stream().forEach(n -> {
            BlnoPaymentDO blnoPaymentDO =
                    BeanUtil.copyProperties(n, BlnoPaymentDO.class, IGNORE_COLUMN);
            blnoPaymentDO.setId(null);
            blnoPaymentDO.setBlnoId(newBlnoId);
            innerPaymentDos.add(blnoPaymentDO);
        });

        blnoVslvoyMapper.insertBatch(innerVslvoyDos);
        blnoScnMapper.insertBatch(innerScnDos);
        blnoPaymentMapper.insertBatch(innerPaymentDos);
        requirementsMapper.insertBatch(innerRrquireDos);
        attachmentMapper.insertBatch(innerAttachmentDos);
        combineDetailMapper.insertBatch(blnoSpiltCombineDetailDos);
        return spiltCombineDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long commitBlnoCombineNew(BlnoCombineVO blnoCombineVO) {
        List<BlnoCombineChildVO> spiltChildVos = blnoCombineVO.getSpiltChildVos();
        BlnoBeCombineVO blnoBeCombineVO = blnoCombineVO.getBlnoBeCombineVO();

        List<BlnoCntrUpdateReqVO> cntrCombineChild = blnoCombineVO.getCntrCombineChild();
        //获取第一条数据的信息
        BlnoInfoVO blnoInfoVO = blnoService.getSiDetail(spiltChildVos.get(0).getBlnoId());
        BlnoBaseInfoVO oldBlnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        //合并校验
        List<Long> oldSiIds = checkCombine(spiltChildVos, cntrCombineChild, false);

        BlnoSpiltCombineDO spiltCombineDO = createBlnoSpilt(BlnoSpiltCombineTypeEnum.COMBINE.getValue(),
                SiSplitCombineApproveStatusEnum.PENDING.getValue());

        //创建新si
        BlnoDO blnoDO = BeanUtil.copyProperties(oldBlnoBasInfoVO, BlnoDO.class, IGNORE_COLUMN);
        blnoDO.setId(null);
        blnoDO.setBlNo(blnoBeCombineVO.getBlNo());
        blnoDO.setSplitCombineType("C");
        blnoDO.setPodBlFlag(false);
        blnoMapper.insert(blnoDO);
        Long newBlnoId = blnoDO.getId();

        //新 si 货物信息
        BlnoCargoDO blnoCargoDO = BeanUtil.copyProperties(blnoInfoVO.getBlnoCargos().get(0), BlnoCargoDO.class, IGNORE_COLUMN);
        blnoCargoDO.setId(null);
        blnoCargoDO.setBlnoId(newBlnoId);
        blnoCargoDO.setQuantity(cntrCombineChild.stream().mapToInt(BlnoCntrUpdateReqVO::getQuantity).sum());
        blnoCargoDO.setGrossWeight(cntrCombineChild.stream().map(BlnoCntrUpdateReqVO::getGrossWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
        blnoCargoDO.setMeasurement(cntrCombineChild.stream().map(BlnoCntrUpdateReqVO::getMeasurement).reduce(BigDecimal.ZERO, BigDecimal::add));
        //网厅可修改货物的唛头，货物描述，中文品名，HSCODE
        if (Objects.equals(SoDataSourceEnum.WEB.getValue(),blnoCombineVO.getCombineDataSource())){
            BlnoCargoUpdateReqVO blnoCargoCombine = blnoCombineVO.getBlnoCargoCombine();
            blnoCargoDO.setMarks(blnoCargoCombine.getMarks());
            blnoCargoDO.setCommodityEn(blnoCargoCombine.getCommodityEn());
            blnoCargoDO.setHsCode(blnoCargoCombine.getHsCode());
            blnoCargoDO.setCommodityCn(blnoCargoCombine.getCommodityCn());
        }
        blnoCargoMapper.insert(blnoCargoDO);

        // 拆合并详情记录
        List<BlnoSpiltCombineDetailDO> blnoSpiltCombineDetailDos = new ArrayList<>();
        oldSiIds.forEach(n -> {
                    BlnoSpiltCombineDetailDO blnoSpiltCombineDetailDO = new BlnoSpiltCombineDetailDO();
                    blnoSpiltCombineDetailDO.setNewBlnoId(newBlnoId);
                    blnoSpiltCombineDetailDO.setOperationId(spiltCombineDO.getId());
                    blnoSpiltCombineDetailDO.setBlnoId(n);
                    blnoSpiltCombineDetailDos.add(blnoSpiltCombineDetailDO);
                }
        );

        //新 si 箱信息
        List<BlnoCntrDO> blnoCntrDos = BeanUtil.copyToList(cntrCombineChild, BlnoCntrDO.class);
        blnoCntrDos.forEach(n -> {
            n.setBlnoId(newBlnoId);
            n.setId(null);
            n.setUpdater(null);
            n.setUpdateTime(null);
            n.setCreator(null);
            n.setCreateTime(null);
        });
        blnoCntrMapper.insertBatch(blnoCntrDos);

        List<RequirementsDO> innerRrquireDos = new ArrayList<>();
        blnoInfoVO.getReqVOList().stream().forEach(n -> {
            RequirementsDO requirementsDO =
                    BeanUtil.copyProperties(n, RequirementsDO.class, IGNORE_COLUMN);
            requirementsDO.setId(null);
            requirementsDO.setReferenceId(newBlnoId);
            innerRrquireDos.add(requirementsDO);
        });
        List<AttachmentDO> innerAttachmentDos = new ArrayList<>();
        if (Objects.equals(SoDataSourceEnum.WEB.getValue(), blnoCombineVO.getCombineDataSource())) {
            if (CollectionUtil.isNotEmpty(blnoCombineVO.getAttachmentCombines())){
                blnoCombineVO.getAttachmentCombines().stream().forEach(n -> {
                    AttachmentDO attachmentDO =
                            BeanUtil.copyProperties(n, AttachmentDO.class, IGNORE_COLUMN);
                    attachmentDO.setId(null);
                    attachmentDO.setReferenceId(newBlnoId);
                    innerAttachmentDos.add(attachmentDO);
                });
            }
        } else {
            blnoInfoVO.getAttachmentReqVO().stream().forEach(n -> {
                AttachmentDO attachmentDO =
                        BeanUtil.copyProperties(n, AttachmentDO.class, IGNORE_COLUMN);
                attachmentDO.setId(null);
                attachmentDO.setReferenceId(newBlnoId);
                innerAttachmentDos.add(attachmentDO);
            });
        }
        List<BlnoVslvoyDO> innerVslvoyDos = new ArrayList<>();
        blnoInfoVO.getBlnoVslvoys().stream().forEach(n -> {
            BlnoVslvoyDO blnoVslvoyDO =
                    BeanUtil.copyProperties(n, BlnoVslvoyDO.class, IGNORE_COLUMN);
            blnoVslvoyDO.setId(null);
            blnoVslvoyDO.setBlnoId(newBlnoId);
            innerVslvoyDos.add(blnoVslvoyDO);
        });

        List<BlnoScnDO> innerScnDos = new ArrayList<>();
        blnoInfoVO.getBlnoScns().stream().forEach(n -> {
            BlnoScnDO blnoScnDO =
                    BeanUtil.copyProperties(n, BlnoScnDO.class, IGNORE_COLUMN);
            blnoScnDO.setId(null);
            blnoScnDO.setBlnoId(newBlnoId);
            innerScnDos.add(blnoScnDO);
        });

        List<BlnoPaymentDO> innerPaymentDos = new ArrayList<>();
        blnoInfoVO.getBlnoPayments().stream().forEach(n -> {
            BlnoPaymentDO blnoPaymentDO =
                    BeanUtil.copyProperties(n, BlnoPaymentDO.class, IGNORE_COLUMN);
            blnoPaymentDO.setId(null);
            blnoPaymentDO.setBlnoId(newBlnoId);
            innerPaymentDos.add(blnoPaymentDO);
        });

        blnoVslvoyMapper.insertBatch(innerVslvoyDos);
        blnoScnMapper.insertBatch(innerScnDos);
        blnoPaymentMapper.insertBatch(innerPaymentDos);
        requirementsMapper.insertBatch(innerRrquireDos);
        if (CollectionUtil.isNotEmpty(innerAttachmentDos)) {
            attachmentMapper.insertBatch(innerAttachmentDos);
        }
        combineDetailMapper.insertBatch(blnoSpiltCombineDetailDos);
        return spiltCombineDO.getId();
    }

    private List<Long> checkCombine(List<BlnoCombineChildVO> spiltChildVos, List<BlnoCntrUpdateReqVO> cntrCombineChild, Boolean approve) {
        if (CollectionUtil.isEmpty(spiltChildVos)) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_NOT_EMPTY);
        }
        spiltChildVos.forEach(n -> {
            if (!StringUtils.equals(n.getSiStatus(), SiStatusEnum.BookingPendingSpace.getValue())
                    && !StringUtils.equals(n.getSiStatus(), SiStatusEnum.AwaitingMaterialUploaded.getValue())) {
                throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SI_STATUS_NOT_SPILT);
            }
        });
        List<Long> blnoIds = spiltChildVos.stream().map(BlnoCombineChildVO::getBlnoId).collect(Collectors.toList());
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoIds).eq(BlnoDO::getPodBlFlag, true).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        List<BlnoCargoDO> cargoDos = blnoCargoMapper.selectList(new LambdaQueryWrapperX<BlnoCargoDO>().in(BlnoCargoDO::getBlnoId, blnoIds));
        List<BlnoCntrDO> blnoCntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapper<BlnoCntrDO>().in(BlnoCntrDO::getBlnoId, blnoIds));
        List<BlnoScnDO> blnoScnDos = blnoScnMapper.selectList(new LambdaQueryWrapper<BlnoScnDO>().in(BlnoScnDO::getBlnoId, blnoIds));
        blnoDos.forEach(n -> {
            if (StringUtils.isNotEmpty(n.getSignType())) {
                throw ServiceExceptionUtil
                        .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SIGNED_NOT_COMBINE);
            }
            if (StringUtils.isNotEmpty(n.getDoStatus())) {
                throw ServiceExceptionUtil
                        .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_DO_NOT_COMBINE);
            }
        });
        if (blnoDos.stream().map(BlnoDO::getRouteId).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_ROUTE_SAME);
        }
        if (blnoDos.stream()
                .map(n -> Arrays.asList(n.getPolCode(), n.getPorCode(), n.getPodCode(), n.getDelCode()))
                .distinct()
                .count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_PORT_CODE_SAME);
        }
        if (blnoDos.stream()
                .map(n -> n.getBkgVesselCode() + n.getBkgVoyage())
                .distinct()
                .count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_VESSEL_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getBkpCode).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_BKP_CODE_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getShippingTerm).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_SHIP_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getAgreementNo).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_AGREEMENT_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getSpaceOwner).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_SPACE_OWNER_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getCntrOwner).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CNTR_OWNER_SAME);
        }

        //● 同一交货地且DRY/AK/DG货物的提单才能进行并单。（DRY 的货和AK的货可以并；DG和非DG货类不能并;DG和DG可以并单 ）；
        //● 两票提单  一票普货 DRY 一票危险品 DG  如果两票不是拼一个箱子就不允许并单。如果两票是拼一个箱子就允许并单。两票分单只有一个箱子，并且两票分单的箱号一样。
        if (cargoDos.stream()
                .anyMatch(n -> !CargoTypeEnum.DRY.getValue().equals(n.getCargoType()) && !CargoTypeEnum.DG.getValue().equals(n.getCargoType())
                        && !CargoTypeEnum.AK.getValue().equals(n.getCargoType()))) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_TYPE_ERROR);
        }
        // 存在DG
        if (cargoDos.stream().anyMatch(n -> CargoTypeEnum.DG.getValue().equals(n.getCargoType()))) {
            if (!cargoDos.stream().allMatch(n -> CargoTypeEnum.DG.getValue().equals(n.getCargoType()))) {
                if (cargoDos.size() != CommonConstants.TWO) {
                    throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_DG_TYPE_ERROR);
                } else {
                    if (!cargoDos.stream().anyMatch(n -> CargoTypeEnum.DRY.getValue().equals(n.getCargoType()))) {
                        throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_DG_TYPE_ERROR);
                    } else {
                        if (blnoCntrDos.stream().map(BlnoCntrDO::getCntrNo).distinct().count() > 1) {
                            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_DG_TYPE_ERROR);
                        }
                    }
                }
            }
        }

        // 判断scn 是否全部一致
        Map<Long, List<BlnoScnDO>> map = blnoScnDos.stream().collect(Collectors.groupingBy(BlnoScnDO::getBlnoId));
        List<List<BlnoScnCombineSameVO>> lists = new ArrayList<>();
        for (Long id : map.keySet()) {
            lists.add(BeanUtil.copyToList(map.get(id), BlnoScnCombineSameVO.class));
        }
        if (!areAllListsEqual(lists)) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_SCN_SAME);
        }

        //原提单 箱的件重尺 和 合并提单 箱的件重尺 汇总 是否一致 判断
        List<BlnoCntrUpdateReqVO> allCntrSpiltChild = new ArrayList<>();
        spiltChildVos.forEach(n -> {
            allCntrSpiltChild.addAll(n.getCntrSpiltChild());
        });
        Map<String, List<BlnoCntrUpdateReqVO>> allCntrMap = allCntrSpiltChild.stream().collect(Collectors.groupingBy(BlnoCntrUpdateReqVO::getCntrNo));
        cntrCombineChild.forEach(n -> {
            List<BlnoCntrUpdateReqVO> originalCntrs = allCntrMap.get(n.getCntrNo());
            if (n.getQuantity().compareTo(originalCntrs.stream().mapToInt(BlnoCntrUpdateReqVO::getQuantity).sum()) != 0) {
                throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_QUANITY_ERROR);
            }
            if (n.getGrossWeight().compareTo(originalCntrs.stream().map(BlnoCntrUpdateReqVO::getGrossWeight).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_WEIGHT_ERROR);
            }
            if (n.getMeasurement().compareTo(originalCntrs.stream().map(BlnoCntrUpdateReqVO::getMeasurement).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_CNTR_MEASUREMENT_ERROR);
            }
        });

        //修改  原数据的拆合并状态   放在校验里面处理 不用重复查询原提单记录
        blnoDos.forEach(n -> {
            n.setSplitCombineType("N->C");
            n.setSiStatus(SiStatusEnum.BookingPendingSpace.getValue());
        });
        blnoMapper.updateBatch(blnoDos);

        return blnoDos.stream().map(BlnoDO::getId).collect(Collectors.toList());
    }

    private <BlnoScnCombineSameVO> boolean areAllListsEqual(List<BlnoScnCombineSameVO>... lists) {
        if (lists == null || lists.length == 0) {
            return true;
        }

        // 如果只有一个List，总是返回true
        if (lists.length == 1) {
            return true;
        }

        // 以第一个List为基准，与其他所有List比较
        List<BlnoScnCombineSameVO> firstList = lists[0];
        for (int i = 1; i < lists.length; i++) {
            if (!CollectionUtils.isEqualCollection(firstList, lists[i])) {
                return false;
            }
        }
        return true;
    }

    @Override
    public List<BlnoSpiltChildVO> getBeCombineData(List<Long> blnoIds) {
        return this.getSpiltChild(blnoIds);
    }

    @Override
    public BlnoSpiltRespVO getSpiltDetail(Long blnoSpiltId) {
        BlnoSpiltRespVO blnoSpiltRespVO = new BlnoSpiltRespVO();
        BlnoSpiltCombineDO spiltCombineDO = blnoSpiltCombineMapper.selectById(blnoSpiltId);
        if (spiltCombineDO == null) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_COMBINE_NOT_EXISTS);
        }

        List<BlnoSpiltCombineDetailDO> spiltDetailDos = combineDetailMapper.selectList(new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>().eq(BlnoSpiltCombineDetailDO::getOperationId, spiltCombineDO.getId()));

        //原提单信息
        BlnoDO blnoDO = blnoMapper.selectById(spiltDetailDos.get(0).getBlnoId());
        List<BlnoCargoDO> cargoDos = blnoCargoMapper.selectList(new LambdaQueryWrapperX<BlnoCargoDO>().eq(BlnoCargoDO::getBlnoId, blnoDO.getId()));
        BlnoCargoDO blnoCargoDO = cargoDos.get(0);

        BlnoBaseInfoVO blnoBaseInfoVO = new BlnoBaseInfoVO();
        BeanUtil.copyProperties(blnoDO, blnoBaseInfoVO);

        BeanUtil.copyProperties(blnoCargoDO, blnoBaseInfoVO);

        blnoSpiltRespVO.setOriginalBaseVO(blnoBaseInfoVO);
        List<BlnoCntrDO> blnoCntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapper<BlnoCntrDO>().eq(BlnoCntrDO::getBlnoId, blnoDO.getId()));
        blnoSpiltRespVO.setOriginalCntrs(BeanUtil.copyToList(blnoCntrDos, BlnoCntrUpdateReqVO.class));
        //拆分的子提单信息
        List<Long> newBlNoIds = spiltDetailDos.stream().map(BlnoSpiltCombineDetailDO::getNewBlnoId).collect(Collectors.toList());
        List<BlnoSpiltChildVO> blnoSpiltChildVos = this.getSpiltChild(newBlNoIds.stream().collect(Collectors.toList()));
        blnoSpiltRespVO.setSpiltChildVos(blnoSpiltChildVos);
        return blnoSpiltRespVO;
    }

    @Override
    public BlnoCombineVO getCombineDetail(Long blnoCombineId) {
        BlnoCombineVO blnoCombineVO = new BlnoCombineVO();
        BlnoSpiltCombineDO spiltCombineDO = blnoSpiltCombineMapper.selectById(blnoCombineId);
        if (spiltCombineDO == null) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_COMBINE_NOT_EXISTS);
        }

        List<BlnoSpiltCombineDetailDO> spiltDetailDos = combineDetailMapper.selectList(new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>().eq(BlnoSpiltCombineDetailDO::getOperationId, spiltCombineDO.getId()));

        //参与合并的子提单信息
        List<Long> oldBlNoIds = spiltDetailDos.stream().map(BlnoSpiltCombineDetailDO::getBlnoId).collect(Collectors.toList());
        List<BlnoCombineChildVO> combineChilds = this.getCombineChild(oldBlNoIds.stream().map(Long::valueOf).collect(Collectors.toList()));
        blnoCombineVO.setSpiltChildVos(combineChilds);
        //合并之后的 提单信息
        BlnoDO blnoDO = blnoMapper.selectById(spiltDetailDos.get(0).getNewBlnoId());
        if (blnoDO != null) {
            blnoCombineVO.setBlnoBeCombineVO(BeanUtil.copyProperties(blnoDO, BlnoBeCombineVO.class));
            List<BlnoCntrDO> blnoCntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapper<BlnoCntrDO>().eq(BlnoCntrDO::getBlnoId, blnoDO.getId()));
            blnoCombineVO.setCntrCombineChild(BeanUtil.copyToList(blnoCntrDos, BlnoCntrUpdateReqVO.class));
        }
        return blnoCombineVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveSpiltOrCombine(Long operationId, Boolean isSpilt,String approveReason) {
        List<BlnoSpiltCombineDetailDO> spiltDetailDos = this.getOperationDetail(operationId, SiSplitCombineApproveStatusEnum.APPROVED.getValue(), approveReason);
        BlnoSpiltCombineDetailDO spiltCombineDetailDO = spiltDetailDos.get(0);
        if (isSpilt) {
            checkScnCpfCode(spiltDetailDos);
            //拆
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .eq(BlnoDO::getId, spiltCombineDetailDO.getBlnoId())
                    .set(BlnoDO::getPodBlFlag, false));
            deleteBlnoPrintLog(true, Arrays.asList(Long.valueOf(spiltCombineDetailDO.getBlnoId())));
            //查询是否是网厅数据
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, spiltDetailDos.stream().map(BlnoSpiltCombineDetailDO::getNewBlnoId).collect(Collectors.toList()))
                    .set(BlnoDO::getPodBlFlag, true));

        } else {
            //并
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .eq(BlnoDO::getId, spiltCombineDetailDO.getNewBlnoId())
                    .set(BlnoDO::getPodBlFlag, true));
            List<Long> oldSiIds = spiltDetailDos.stream().map(n -> Long.valueOf(n.getBlnoId())).collect(Collectors.toList());
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, oldSiIds)
                    .set(BlnoDO::getPodBlFlag, false));
            deleteBlnoPrintLog(true, oldSiIds);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rejectSpiltOrCombine(Long operationId, Boolean isSpilt,String approveReason) {
        List<BlnoSpiltCombineDetailDO> operationDetail = this.getOperationDetail(operationId, SiSplitCombineApproveStatusEnum.REJECTED.getValue(), approveReason);
        BlnoSpiltCombineDetailDO spiltCombineDetailDO = operationDetail.get(0);
        if (isSpilt) {
            //拆
            BlnoDO blnoDO = blnoMapper.selectById(spiltCombineDetailDO.getBlnoId());
            if (blnoDO == null) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
            }
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .eq(BlnoDO::getId, spiltCombineDetailDO.getBlnoId())
                    .set(BlnoDO::getSplitCombineType, SiSplitCombineEnum.NORMAL_SPLIT.getValue().equals(blnoDO.getSplitCombineType()) ? "N" : "C"));
            blnoMapper.delete(new LambdaQueryWrapper<BlnoDO>()
                    .in(BlnoDO::getId, operationDetail.stream().map(BlnoSpiltCombineDetailDO::getNewBlnoId).collect(Collectors.toList())));
        } else {
            //并
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, operationDetail.stream().map(BlnoSpiltCombineDetailDO::getBlnoId).collect(Collectors.toList()))
                    .set(BlnoDO::getSplitCombineType, "N"));
            blnoMapper.delete(new LambdaQueryWrapper<BlnoDO>()
                    .eq(BlnoDO::getId, spiltCombineDetailDO.getNewBlnoId()));
        }
    }

    @Override
    public BlnoSplitInfoDto getSplitBlnoDetail(Long blnoId) {
        BlnoInfoVO blnoInfoVO = blnoService.getSiDetail(blnoId);
        BlnoSplitInfoDto blnoSplitInfoDto = new BlnoSplitInfoDto();
        //基础信息
        BlnoBaseInfoDto blnoBaseInfo = BlnoConvert.INSTANCE.convertBlnoBaseInfoDto(blnoInfoVO.getBlnoBasInfoVO());
        SoDO soDO = soMapper.selectOne(new LambdaQueryWrapperX<SoDO>()
                .eqIfPresent(SoDO::getBlNo, blnoBaseInfo.getBlNo())
                .eqIfPresent(SoDO::getBookingNo, blnoBaseInfo.getBookingNo())
                .eqIfPresent(SoDO::getValidStatus, ValidStatusEnum.DEFAULT_VALID.getValue())
                .neIfPresent(SoDO::getSoStatus, SoStatusEnum.BookingDraft.getValue())
                .last("limit 1")
        );
        if (soDO != null) {
            blnoBaseInfo.setSoId(soDO.getId());
        }
        //原单基础信息
        blnoSplitInfoDto.setOriginalBaseVO(blnoBaseInfo);
        //原单常用收发通
        List<BlnoScnUpdateReqDto> blnoScnUpdateReqs = BlnoScnConvert.INSTANCE.convertBlnoScnUpdateReqDtoVo(blnoInfoVO.getBlnoScns());
        blnoSplitInfoDto.setOriginalScns(blnoScnUpdateReqs);
        //原单货物信息
        List<BlnoCargoUpdateReqDto> blnoCargos = BlnoCargoConvert.INSTANCE.convertBlnoCargoUpdateReqDto(blnoInfoVO.getBlnoCargos());
        blnoSplitInfoDto.setOriginalCargos(blnoCargos);
        //原单航线信息
        List<BlnoVslvoyUpdateReqDto> blnoVslvoys = BlnoVslvoyConvert.INSTANCE.convertBlnoVslvoyUpdateReqDto(blnoInfoVO.getBlnoVslvoys());
        blnoService.setShippingScheduleTime(blnoVslvoys);
        blnoSplitInfoDto.setOriginalVslvoys(blnoVslvoys);
        //原单箱信息
        List<BlnoCntrUpdateReqDto> blnoCntrs = BlnoCntrConvert.INSTANCE.convertBlnoCntrUpdateReqDto(blnoInfoVO.getBlnoCntrs());
        //查询原单箱的VGM信息
        VgmQueryInfoVO request =new VgmQueryInfoVO();
        request.setVoyage(blnoBaseInfo.getBkgVoyage());
        request.setVesselCode(blnoBaseInfo.getBkgVesselCode());
        request.setCntrNo(blnoCntrs.stream().map(BlnoCntrUpdateReqDto::getCntrNo).collect(Collectors.toList()));
        List<VgmDO> vgmList = vgmService.getVgmInfo(request);
        blnoSplitInfoDto.setVgmRespDtos(VgmConvert.INSTANCE.convertVgmRespDto(vgmList));

        blnoSplitInfoDto.setOriginalCntrs(blnoCntrs);
        //原单的支付方式信息
        List<BlnoPaymentUpdateReqDto> blnoPayments = BlnoPaymentConvert.INSTANCE.convertBlnoPaymentUpdateReqDto(blnoInfoVO.getBlnoPayments());
        blnoSplitInfoDto.setOriginalPayments(blnoPayments);
        //附件信息
        List<AttachmentUpdateReqDto> attachmentReqs = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqVO(blnoInfoVO.getAttachmentReqVO());
        blnoSplitInfoDto.setOriginalAttachments(attachmentReqs);
        //附加要求
        List<RequirementsUpdateReqDto> reqList = RequirementsConvert.INSTANCE.convertRequirementsUpdateReqVO(blnoInfoVO.getReqVOList());
        blnoSplitInfoDto.setOriginalRequirements(reqList);

        List<BlnoSpiltCombineDetailDO> spiltDetailDos = combineDetailMapper.selectList(
                new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>().eq(BlnoSpiltCombineDetailDO::getBlnoId, blnoId));
        if (CollectionUtil.isEmpty(spiltDetailDos)) {
            //展示拆单按钮
            blnoSplitInfoDto.setSplitButton(Boolean.TRUE);
            blnoSplitInfoDto.setSplitStatus(Boolean.FALSE);
            return blnoSplitInfoDto;
        }
        List<Long> newBlnoIds = spiltDetailDos.stream().map(BlnoSpiltCombineDetailDO::getNewBlnoId).collect(Collectors.toList());
        if (Objects.equals(SiSplitCombineEnum.NORMAL.getValue(), blnoBaseInfo.getSplitCombineType())
                || Objects.equals(SiSplitCombineEnum.COMBINE.getValue(), blnoBaseInfo.getSplitCombineType())) {
            blnoSplitInfoDto.setSplitButton(Boolean.TRUE);
            blnoSplitInfoDto.setSplitStatus(Boolean.FALSE);
        } else {
            blnoSplitInfoDto.setSplitButton(Boolean.FALSE);
            blnoSplitInfoDto.setSplitStatus(Boolean.TRUE);
        }
        if (CollectionUtil.isNotEmpty(newBlnoIds)) {
            List<BlnoSpiltChildVO> blnoSpiltChildVos = this.getSpiltChild(newBlnoIds.stream().map(Long::valueOf).collect(Collectors.toList()));
            List<BlnoSpiltChildDto> blnoSpiltChildDtos = BeanUtil.copyToList(blnoSpiltChildVos, BlnoSpiltChildDto.class);
            blnoSpiltChildDtos.forEach(entity->{
                blnoService.setShippingScheduleTime(entity.getVslvoySpiltChild());
            });
            //拆分单的信息
            blnoSplitInfoDto.setSpiltChildDtos(blnoSpiltChildDtos);

        }
        return blnoSplitInfoDto;
    }

    @Override
    public void checkOriginalSpiltData(Long blnoId) {
        BlnoDO blnoDO = blnoMapper.selectById(blnoId);
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        if (!StringUtils.equals(SPLIT_COMBINE_TYPE_N, blnoDO.getSplitCombineType())
                && !StringUtils.equals(SPLIT_COMBINE_TYPE_C, blnoDO.getSplitCombineType())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_STATUS_ERROR);
        }
        if (!StringUtils.equals(blnoDO.getSiStatus(), SiStatusEnum.BookingPendingSpace.getValue())
                && !StringUtils.equals(blnoDO.getSiStatus(), SiStatusEnum.AwaitingMaterialUploaded.getValue())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SI_STATUS_NOT_SPILT);
        }
        if (StringUtils.isNotEmpty(blnoDO.getSignType())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SIGNED_NOT_SPILT);
        }
        if (StringUtils.isNotEmpty(blnoDO.getDoStatus())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_DO_NOT_SPILT);
        }
    }

    @Override
    public void checkOriginalCombineData(List<Long> blnoIds) {
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoIds).eq(BlnoDO::getPodBlFlag, true).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        if (CollectionUtil.isEmpty(blnoDos)) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_NOT_EMPTY);
        }
        // 如果存在任何一个Blno的splitCombineType不等于N(原始单)
        if (blnoDos.stream().anyMatch(e -> !Objects.equals(SPLIT_COMBINE_TYPE_N, e.getSplitCombineType()))) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_STATUS_ERROR);
        }
        blnoDos.forEach(n -> {
            if (!StringUtils.equals(n.getSiStatus(), SiStatusEnum.BookingPendingSpace.getValue())
                    && !StringUtils.equals(n.getSiStatus(), SiStatusEnum.AwaitingMaterialUploaded.getValue())) {
                throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SI_STATUS_NOT_SPILT);
            }
        });

        List<BlnoCargoDO> cargoDos = blnoCargoMapper.selectList(new LambdaQueryWrapperX<BlnoCargoDO>().in(BlnoCargoDO::getBlnoId, blnoIds));
        List<BlnoCntrDO> blnoCntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapper<BlnoCntrDO>().in(BlnoCntrDO::getBlnoId, blnoIds));
        List<BlnoScnDO> blnoScnDos = blnoScnMapper.selectList(new LambdaQueryWrapper<BlnoScnDO>().in(BlnoScnDO::getBlnoId, blnoIds));
        blnoDos.forEach(n -> {
            if (StringUtils.isNotEmpty(n.getSignType())) {
                throw ServiceExceptionUtil
                        .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SIGNED_NOT_COMBINE);
            }
            if (StringUtils.isNotEmpty(n.getDoStatus())) {
                throw ServiceExceptionUtil
                        .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_DO_NOT_COMBINE);
            }
        });
        if (blnoDos.stream().map(BlnoDO::getRouteId).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_ROUTE_SAME);
        }
        if (blnoDos.stream()
                .map(n -> Arrays.asList(n.getPolCode(), n.getPorCode(), n.getPodCode(), n.getDelCode()))
                .distinct()
                .count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_PORT_CODE_SAME);
        }
        if (blnoDos.stream()
                .map(n -> n.getBkgVesselCode() + n.getBkgVoyage())
                .distinct()
                .count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_VESSEL_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getBkpCode).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_BKP_CODE_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getShippingTerm).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_SHIP_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getAgreementNo).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_AGREEMENT_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getSpaceOwner).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_SPACE_OWNER_SAME);
        }
        if (blnoDos.stream().map(BlnoDO::getCntrOwner).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CNTR_OWNER_SAME);
        }

        //● 同一交货地且DRY/AK/DG货物的提单才能进行并单。（DRY 的货和AK的货可以并；DG和非DG货类不能并;DG和DG可以并单 ）；
        //● 两票提单  一票普货 DRY 一票危险品 DG  如果两票不是拼一个箱子就不允许并单。如果两票是拼一个箱子就允许并单。两票分单只有一个箱子，并且两票分单的箱号一样。
        if (cargoDos.stream()
                .anyMatch(n -> !CargoTypeEnum.DRY.getValue().equals(n.getCargoType()) && !CargoTypeEnum.DG.getValue().equals(n.getCargoType())
                        && !CargoTypeEnum.AK.getValue().equals(n.getCargoType()))) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_TYPE_ERROR);
        }
        // 存在DG
        if (cargoDos.stream().anyMatch(n -> CargoTypeEnum.DG.getValue().equals(n.getCargoType()))) {
            if (!cargoDos.stream().allMatch(n -> CargoTypeEnum.DG.getValue().equals(n.getCargoType()))) {
                if (cargoDos.size() != CommonConstants.TWO) {
                    throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_DG_TYPE_ERROR);
                } else {
                    if (!cargoDos.stream().anyMatch(n -> CargoTypeEnum.DRY.getValue().equals(n.getCargoType()))) {
                        throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_DG_TYPE_ERROR);
                    } else {
                        if (blnoCntrDos.stream().map(BlnoCntrDO::getCntrNo).distinct().count() > 1) {
                            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_CARGO_DG_TYPE_ERROR);
                        }
                    }
                }
            }
        }

        // 判断scn 是否全部一致
        Map<Long, List<BlnoScnDO>> map = blnoScnDos.stream().collect(Collectors.groupingBy(BlnoScnDO::getBlnoId));
        List<List<BlnoScnCombineSameVO>> lists = new ArrayList<>();
        for (Long id : map.keySet()) {
            lists.add(BeanUtil.copyToList(map.get(id), BlnoScnCombineSameVO.class));
        }
        if (!areAllListsEqual(lists)) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_LIST_SCN_SAME);
        }
    }

    @Override
    public List<BlnoCombineListDto> listBlnoCombine(Long blnoId) throws Exception {
        BlnoInfoVO blnoInfoVO = blnoService.getSiDetail(blnoId);
        BlnoBaseInfoVO blnoBaseInfo = blnoInfoVO.getBlnoBasInfoVO();
        //查询货类信息
        List<BlnoCargoDO> blnoCargos = blnoCargoMapper.selectList(new LambdaQueryWrapperX<BlnoCargoDO>().eq(BlnoCargoDO::getBlnoId, blnoId));
        String cargoType=blnoCargos.get(0).getCargoType();
        //货类允许DRY、FQ、AK、DG的提单才能进行并单
        List<String> cargoTypes=Arrays.asList(CargoTypeEnum.DRY.getValue(),CargoTypeEnum.FQ.getValue(),CargoTypeEnum.AK.getValue(),CargoTypeEnum.DG.getValue());
        if (!cargoTypes.contains(cargoType)) {
            return Collections.emptyList();
        }
        //货类为DRY,子类必须为空
        if (Objects.equals(cargoType, CargoTypeEnum.DRY.getValue()) && StringUtils.isNotEmpty(blnoCargos.get(0).getCargoSubType())) {
            return Collections.emptyList();
        }
        /*List<String> combineStatus = Arrays.asList(SiStatusEnum.BookingPendingSpace.getValue(),
                SiStatusEnum.AwaitingMaterialUploaded.getValue(), SiStatusEnum.BookingConfirmed.getValue());*/
        LambdaQueryWrapperX<BlnoDO> wrapper = new LambdaQueryWrapperX<BlnoDO>()
                .eqIfPresent(BlnoDO::getPodBlFlag, true)
                .eqIfPresent(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                .eqIfPresent(BlnoDO::getRouteId, blnoBaseInfo.getRouteId())
                .eqIfPresent(BlnoDO::getPolCode, blnoBaseInfo.getPolCode()).eqIfPresent(BlnoDO::getPorCode, blnoBaseInfo.getPorCode())
                .eqIfPresent(BlnoDO::getPodCode, blnoBaseInfo.getPodCode()).eqIfPresent(BlnoDO::getDelCode, blnoBaseInfo.getDelCode())
                .eqIfPresent(BlnoDO::getBkgVesselCode, blnoBaseInfo.getBkgVesselCode()).eqIfPresent(BlnoDO::getBkgVoyage, blnoBaseInfo.getBkgVoyage())
                .eqIfPresent(BlnoDO::getBkpCode, blnoBaseInfo.getBkpCode()).eqIfPresent(BlnoDO::getShippingTerm, blnoBaseInfo.getShippingTerm()).eqIfPresent(BlnoDO::getAgreementNo, blnoBaseInfo.getAgreementNo())
                .eqIfPresent(BlnoDO::getSpaceOwner, blnoBaseInfo.getSpaceOwner()).eqIfPresent(BlnoDO::getCntrOwner, blnoBaseInfo.getCntrOwner())
                .eqIfPresent(BlnoDO::getBkpCode,blnoBaseInfo.getBkpCode())
                .eqIfPresent(BlnoDO::getSplitCombineType, SiSplitCombineEnum.NORMAL.getValue())
                .inIfPresent(BlnoDO::getSiStatus, CAN_SPLIT_COMBINE_BLNO_STATUS);
        List<BlnoDO> blnoDos = blnoMapper.selectList(wrapper);
        log.info("符合合并条件的提单:{}", JSONObject.toJSONString(blnoDos));

        if (CollectionUtil.isEmpty(blnoDos)) {
            return Collections.emptyList();
        }
        //查询收发通收货人信息
        BlnoScnDO blnoScn = blnoScnMapper.selectOne(new LambdaQueryWrapperX<BlnoScnDO>()
                .eq(BlnoScnDO::getBlnoId, blnoId).eq(BlnoScnDO::getPartyTypeCode, PartyTypeCodeEnum.CONSIGNEE.getValue()).last("LIMIT 1"));

        if (CollectionUtil.isEmpty(blnoCargos)||Objects.isNull(blnoScn)) {
            return Collections.emptyList();
        }
        log.info("合并条件(收发通一致)的提单:{}", JSONObject.toJSONString(blnoDos));
        //企业唯一识别码前缀和后缀
        String uniqueIdentifierPre=blnoScn.getUniqueIdentifierPre();
        String uniqueIdentifierFix=blnoScn.getUniqueIdentifierFix();
        List<Long> blnoIds=blnoDos.stream().map(BlnoDO::getId).collect(Collectors.toList());
        //收货人一致
        List<BlnoScnDO> blnoScns = blnoScnMapper.selectList(new LambdaQueryWrapperX<BlnoScnDO>()
                .eq(BlnoScnDO::getUniqueIdentifierPre, uniqueIdentifierPre)
                .eq(BlnoScnDO::getUniqueIdentifierFix, uniqueIdentifierFix)
                .eq(BlnoScnDO::getPartyTypeCode, PartyTypeCodeEnum.CONSIGNEE.getValue())
                .inIfPresent(BlnoScnDO::getBlnoId, blnoIds));
        if (CollectionUtil.isEmpty(blnoScns)){
            return Collections.emptyList();
        }
        Set<Long> scnBlnoIds = blnoScns.stream().map(BlnoScnDO::getBlnoId).collect(Collectors.toSet());
        log.info("合并条件(收发通收货人一致)的提单:{}", JSONObject.toJSONString(scnBlnoIds));
        //允许合并的提单
        List<BlnoCombineListDto> allowCombineBlnoDtos = blnoDos.stream()
                .filter(e -> StringUtils.isEmpty(e.getSignType()))
                .filter(e -> StringUtils.isEmpty(e.getDoStatus()))
                .map(e -> {
                    return BeanUtil.copyProperties(e, BlnoCombineListDto.class);
                }).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(allowCombineBlnoDtos)) {
            return Collections.emptyList();
        }
        allowCombineBlnoDtos.removeIf(p->!scnBlnoIds.contains(p.getId()));
        List<BlnoCombineListDto> allowCombineBlnos = allowCombineBlnoDtos.stream().sorted(Comparator.comparing((BlnoCombineListDto p) -> !Objects.equals(blnoId, p.getId()))
                .thenComparing(BlnoCombineListDto::getCreateTime)).collect(Collectors.toList());
        log.info("最终允许合并的提单:{}", JSONObject.toJSONString(allowCombineBlnoDtos));
        //查询提单附属
        List<Long> allowBlnoId = allowCombineBlnos.stream().map(BlnoCombineListDto::getId).collect(Collectors.toList());

        //开启并行任务处理
        Map<Long, List<BlnoVslvoyDO>> vslvoyMap = queryToMap(
                () -> blnoVslvoyMapper.selectList(new LambdaQueryWrapperX<BlnoVslvoyDO>().in(BlnoVslvoyDO::getBlnoId, allowBlnoId)),
                BlnoVslvoyDO::getBlnoId
        );
        //只能合并同种货类SI,后续可以变更需求
        Map<Long, List<BlnoCargoDO>> cargoMap = queryToMap(
                () -> blnoCargoMapper.selectList(new LambdaQueryWrapperX<BlnoCargoDO>().in(BlnoCargoDO::getBlnoId, allowBlnoId)
                        .eq(BlnoCargoDO::getCargoType,cargoType).isNull(BlnoCargoDO::getCargoSubType)),
                BlnoCargoDO::getBlnoId
        );
        Map<Long, List<BlnoCntrDO>> cntrMap = queryToMap(
                () -> blnoCntrMapper.selectList(new LambdaQueryWrapperX<BlnoCntrDO>().in(BlnoCntrDO::getBlnoId, allowBlnoId)),
                BlnoCntrDO::getBlnoId
        );
        Map<Long, List<BlnoScnDO>> scnMap = queryToMap(
                () -> blnoScnMapper.selectList(new LambdaQueryWrapperX<BlnoScnDO>().in(BlnoScnDO::getBlnoId, allowBlnoId)),
                BlnoScnDO::getBlnoId
        );
        Map<Long, List<BlnoPaymentDO>> payMentMap = queryToMap(
                () -> blnoPaymentMapper.selectList(new LambdaQueryWrapperX<BlnoPaymentDO>().in(BlnoPaymentDO::getBlnoId, allowBlnoId)),
                BlnoPaymentDO::getBlnoId
        );
        Map<Long, List<RequirementsDO>> requireMentMap = queryToMap(
                () -> requirementsMapper.selectList(new LambdaQueryWrapperX<RequirementsDO>().in(RequirementsDO::getReferenceId, allowBlnoId)),
                RequirementsDO::getReferenceId
        );
        Map<Long, List<AttachmentDO>> attachmentMap = queryToMap(
                () -> attachmentMapper.selectList(new LambdaQueryWrapperX<AttachmentDO>().in(AttachmentDO::getReferenceId, allowBlnoId).eq(AttachmentDO::getReferenceType, SoSiEnum.SI.getValue())),
                AttachmentDO::getReferenceId
        );

        List<String> blNos = allowCombineBlnos.stream().map(BlnoCombineListDto::getBlNo).collect(Collectors.toList());
        // 查询对应soids
        Map<Long, List<SoForecastBaseVO>> soForecastMap = new HashMap<>(16);
        List<SoDO> soList = soMapper.selectList(new LambdaQueryWrapperX<SoDO>().in(SoDO::getBlNo, blNos).eq(SoDO::getValidStatus,ValidStatusEnum.DEFAULT_VALID.getValue()));
        Map<String,Long> soIdBlMap = soList.stream().collect(Collectors.toMap(SoDO::getBookingNo,SoDO::getId));
        if(CollectionUtil.isNotEmpty(soList)){
            List<Long> soIds = soList.stream().map(SoDO::getId).collect(Collectors.toList());
            List<SoForecastBaseVO> soForecastList = soSplitCombineService.getSoForecast(soIds.stream().distinct().collect(Collectors.toList()));
            soForecastMap = queryToMap(() -> soForecastList, SoForecastBaseVO::getSoId);
        }
        Map<Long, List<SoForecastBaseVO>> finalSoForecastMap = soForecastMap;
        allowCombineBlnos.forEach(e -> {
            e.setBlnoVslvoys(BeanUtil.copyToList(vslvoyMap.get(e.getId()), BlnoVslvoyUpdateReqDto.class));
            if (CollectionUtil.isNotEmpty(e.getBlnoVslvoys())) {
                List<BlnoVslvoyUpdateReqDto> blnoVslvoys = e.getBlnoVslvoys();
                blnoVslvoys.sort(Comparator.comparingInt(BlnoVslvoyUpdateReqDto::getRouteNo));
                BlnoVslvoyUpdateReqDto vsloy = blnoVslvoys.stream()
                        .filter(entity -> Objects.equals("LINER", entity.getTransmode()))
                        .findFirst().orElse(null);
                e.setLinerVsloy(vsloy);
            }
            blnoService.setShippingScheduleTime(e.getBlnoVslvoys());
            e.setBlnoCntrs(BeanUtil.copyToList(cntrMap.get(e.getId()), BlnoCntrUpdateReqDto.class));
            e.setBlnoCargos(BeanUtil.copyToList(cargoMap.get(e.getId()), BlnoCargoUpdateReqDto.class));
            e.setBlnoPayments(BeanUtil.copyToList(payMentMap.get(e.getId()), BlnoPaymentUpdateReqDto.class));
            e.setBlnoScns(BeanUtil.copyToList(scnMap.get(e.getId()), BlnoScnUpdateReqDto.class));
            e.setReqVOList(BeanUtil.copyToList(requireMentMap.get(e.getId()), RequirementsUpdateReqDto.class));
            e.setAttachmentReqVO(BeanUtil.copyToList(attachmentMap.get(e.getId()), AttachmentUpdateReqDto.class));
            e.setSoForecastList(finalSoForecastMap.get(soIdBlMap.get(e.getBookingNo())));
        });

        return allowCombineBlnos.stream().filter(e->CollectionUtil.isNotEmpty(e.getBlnoCargos())).collect(Collectors.toList());
    }


    private List<BlnoSpiltCombineDetailDO> getOperationDetail(Long operationId, String operationType,String approveReason) {
        BlnoSpiltCombineDO spiltCombineDO = blnoSpiltCombineMapper.selectById(operationId);
        if (spiltCombineDO == null) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SPILT_COMBINE_NOT_EXISTS);
        }
        if (!SiSplitCombineApproveStatusEnum.PENDING.getValue().equals(spiltCombineDO.getApproveStatus())) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_IS_NOT_PENDING);
        }
        List<BlnoSpiltCombineDetailDO> spiltDetailDos = combineDetailMapper.selectList(new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>().eq(BlnoSpiltCombineDetailDO::getOperationId, spiltCombineDO.getId()));
        if (CollectionUtil.isEmpty(spiltDetailDos)) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SON_NULL_SPILT);
        }
        spiltCombineDO.setApproveStatus(operationType);
        spiltCombineDO.setApproveTime(LocalDateTime.now());
        spiltCombineDO.setApproveUser(String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        spiltCombineDO.setApproveReason(approveReason);
        blnoSpiltCombineMapper.updateById(spiltCombineDO);
        return spiltDetailDos;
    }

    private List<BlnoSpiltChildVO> getSpiltChild(List<Long> blnoIds) {
        if (CollectionUtil.isEmpty(blnoIds)) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SON_NULL_SPILT);
        }
        List<BlnoSpiltChildVO> blnoSpiltChildVos = new ArrayList<>();
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>().in(BlnoDO::getId, blnoIds).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        if (CollectionUtil.isNotEmpty(blnoDos)) {
            List<BlnoCargoDO> cargoDos = blnoCargoMapper.selectList(new LambdaQueryWrapperX<BlnoCargoDO>().inIfPresent(BlnoCargoDO::getBlnoId, blnoIds));
            Map<Long, List<BlnoCargoDO>> cargoMap = cargoDos.stream().collect(Collectors.groupingBy(BlnoCargoDO::getBlnoId));
            List<BlnoCntrDO> newCntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapperX<BlnoCntrDO>().inIfPresent(BlnoCntrDO::getBlnoId, blnoIds));
            Map<Long, List<BlnoCntrDO>> cntrMap = newCntrDos.stream().collect(Collectors.groupingBy(BlnoCntrDO::getBlnoId));
            //收发通
            List<BlnoScnDO> newScnDos = blnoScnMapper.selectList(new LambdaQueryWrapperX<BlnoScnDO>().inIfPresent(BlnoScnDO::getBlnoId, blnoIds));
            Map<Long, List<BlnoScnDO>> scnMap = newScnDos.stream().collect(Collectors.groupingBy(BlnoScnDO::getBlnoId));
            //付款方式
            List<BlnoPaymentDO> newBlnoPayMents = blnoPaymentMapper.selectList(new LambdaQueryWrapperX<BlnoPaymentDO>().inIfPresent(BlnoPaymentDO::getBlnoId, blnoIds));
            Map<Long, List<BlnoPaymentDO>> payMentMap = newBlnoPayMents.stream().collect(Collectors.groupingBy(BlnoPaymentDO::getBlnoId));
            //附件信息
            List<AttachmentDO> attachmentList = attachmentMapper.selectList(new LambdaQueryWrapperX<AttachmentDO>().inIfPresent(AttachmentDO::getReferenceId, blnoIds)
                    .eq(AttachmentDO::getReferenceType, SoSiEnum.SI.getValue()));
            Map<Long,List<AttachmentDO>> attachMentMap=attachmentList.stream().collect(Collectors.groupingBy(AttachmentDO::getReferenceId));

            //附加要求
            List<RequirementsDO> requirementsDos = requirementsMapper.selectList(new LambdaQueryWrapperX<RequirementsDO>().inIfPresent(RequirementsDO::getReferenceId,blnoIds)
                    .eq(RequirementsDO::getReferenceType, SoSiEnum.SI.getValue()));
            Map<Long,List<RequirementsDO>> requirementsMap=requirementsDos.stream().collect(Collectors.groupingBy(RequirementsDO::getReferenceId));

            //航线船期
            List<BlnoVslvoyDO> blnoVslvoyDos = blnoVslvoyMapper.selectList(new LambdaQueryWrapperX<BlnoVslvoyDO>().inIfPresent(BlnoVslvoyDO::getBlnoId, blnoIds));
            Map<Long, List<BlnoVslvoyDO>> vslvoyMap = blnoVslvoyDos.stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));

            blnoDos.forEach(element -> {
                BlnoSpiltChildVO blnoSpiltChildVO = new BlnoSpiltChildVO();
                BeanUtil.copyProperties(element, blnoSpiltChildVO);
                blnoSpiltChildVO.setBlnoId(element.getId());

                List<BlnoCargoDO> singleCargos = cargoMap.get(element.getId());
                BlnoCargoDO blnoCargoDO = singleCargos.get(0);
                blnoSpiltChildVO.setCargoChild(BeanUtil.copyProperties(blnoCargoDO, BlnoCargoUpdateReqVO.class));

                List<BlnoCntrDO> cntrDos = cntrMap.get(element.getId());
                blnoSpiltChildVO.setCntrSpiltChild(BeanUtil.copyToList(cntrDos, BlnoCntrUpdateReqVO.class));

                List<BlnoScnDO> scnDos = scnMap.get(element.getId());
                blnoSpiltChildVO.setScnSpiltChild(BeanUtil.copyToList(scnDos, BlnoScnUpdateReqVO.class));

                List<BlnoPaymentDO> paymentDos = payMentMap.get(element.getId());
                blnoSpiltChildVO.setPaymentSpiltChild(BeanUtil.copyToList(paymentDos, BlnoPaymentUpdateReqVO.class));

                List<AttachmentDO> attachmentDos = attachMentMap.get(element.getId());
                blnoSpiltChildVO.setAttachmentsSpiltChild(BeanUtil.copyToList(attachmentDos, AttachmentUpdateReqVO.class));

                List<RequirementsDO> requirements = requirementsMap.get(element.getId());
                blnoSpiltChildVO.setRequirementsSpiltChild(BeanUtil.copyToList(requirements, RequirementsUpdateReqVO.class));

                List<BlnoVslvoyDO> vslvoyDos = vslvoyMap.get(element.getId());
                blnoSpiltChildVO.setVslvoySpiltChild(BeanUtil.copyToList(vslvoyDos, BlnoVslvoyUpdateReqVO.class));

                blnoSpiltChildVos.add(blnoSpiltChildVO);
            });
        }
        return blnoSpiltChildVos;
    }

    private List<BlnoCombineChildVO> getCombineChild(List<Long> blnoIds) {
        if (CollectionUtil.isEmpty(blnoIds)) {
            throw ServiceExceptionUtil.exception(BlnoSpiltCombineErrorCodeConstants.BLNO_SON_NULL_SPILT);
        }
        List<BlnoCombineChildVO> blnoCombineChildVos = new ArrayList<>();
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>().in(BlnoDO::getId, blnoIds).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        if (CollectionUtil.isNotEmpty(blnoDos)) {

            List<BlnoCntrDO> newCntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapperX<BlnoCntrDO>().inIfPresent(BlnoCntrDO::getBlnoId, blnoIds));
            Map<Long, List<BlnoCntrDO>> cntrMap = newCntrDos.stream().collect(Collectors.groupingBy(BlnoCntrDO::getBlnoId));

            List<BlnoCargoDO> cargoDos = blnoCargoMapper.selectList(new LambdaQueryWrapperX<BlnoCargoDO>().inIfPresent(BlnoCargoDO::getBlnoId, blnoIds));
            Map<Long, List<BlnoCargoDO>> cargoMap = cargoDos.stream().collect(Collectors.groupingBy(BlnoCargoDO::getBlnoId));

            blnoDos.forEach(element -> {
                BlnoCombineChildVO combineChildVO = new BlnoCombineChildVO();
                BeanUtil.copyProperties(element, combineChildVO);

                List<BlnoCargoDO> singleCargos = cargoMap.get(element.getId());
                BlnoCargoDO blnoCargoDO = singleCargos.get(0);
                BeanUtil.copyProperties(blnoCargoDO,combineChildVO);

                List<BlnoCntrDO> cntrDos = cntrMap.get(element.getId());
                combineChildVO.setCntrSpiltChild(BeanUtil.copyToList(cntrDos, BlnoCntrUpdateReqVO.class));


                blnoCombineChildVos.add(combineChildVO);
            });
        }
        return blnoCombineChildVos;
    }

    /**
     * 并行流处理
     *
     * @param querySupplier 入参
     * @param keyExtractor  入参
     * @param <T>           泛型
     * @return 返回
     */
    private <T> Map<Long, List<T>> queryToMap(Supplier<List<T>> querySupplier, Function<T, Long> keyExtractor) {
        return querySupplier.get()
                .parallelStream()
                .collect(Collectors.groupingBy(keyExtractor));
    }

    /**
     * 网厅拆分审核通过校验
     *
     * @param spiltDetailDos
     */
    private void checkScnCpfCode(List<BlnoSpiltCombineDetailDO> spiltDetailDos) {
        BlnoSpiltCombineDetailDO spiltCombineDetailDO = spiltDetailDos.get(0);
        BlnoDO blnoDO = blnoMapper.selectById(spiltCombineDetailDO.getBlnoId());
        if (Objects.equals(SoDataSourceEnum.WEB.getValue(), blnoDO.getDatasource())) {
            List<Long> newBlnoIds = spiltDetailDos.stream().map(BlnoSpiltCombineDetailDO::getNewBlnoId).collect(Collectors.toList());
            //查询收发通信息
            List<BlnoScnDO> blnoScnList = blnoScnMapper.selectList(new LambdaQueryWrapperX<BlnoScnDO>().inIfPresent(BlnoScnDO::getBlnoId, newBlnoIds));
            List<BlnoScnUpdateReqVO> blnoScns = BeanUtil.copyToList(blnoScnList, BlnoScnUpdateReqVO.class);
            blnoService.checkCpfCode(blnoScns);
        }
    }

}
